package main

import (
	"embed"
	"fmt"
	"io/fs"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	// "github.com/godruoyi/go-snowflake"
	// "example.com/card/bot"
)

//go:embed web/dist/*
var web embed.FS

/*

游戏服务器
- 管理、回收房间（房间存活时间） 线程扫描房间，存活时间-1；
- 房间状态【组队中】---开始游戏--- 【游戏中】
-

玩家：
- 为了简单起见，不设计用户、权限系统。
- id: 浏览器随机生成uuid。保存在浏览器本地
- name: 用户进入房间必填。保存在浏览器本地

玩家操作
- 组队阶段：入座、离座
- 游戏阶段：下注、弃牌

*/

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method

		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
		c.Header("Access-Control-Allow-Headers", "*")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")

		//放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		// 处理请求
		c.Next()
	}
}

type FileSystems []http.FileSystem

func serve1() {
	// use static files
	f := http.Dir("./web/dist/")
	http.Handle("/", http.FileServer(f))
	http.ListenAndServe(":8000", nil)
}

func serve2() {
	// use embed FS
	fsub, _ := fs.Sub(web, "web/dist")
	f := http.FS(fsub)
	http.Handle("/", http.FileServer(f))
	http.ListenAndServe(":8000", nil)
}

func main() {

	go serve2()

	ticker1 := time.NewTicker(1 * time.Second)
	go func(t *time.Ticker) {
		for {
			// 每1秒从chan t.C 中读取一次
			<-t.C
			for i := 0; i < len(rooms); i++ {
				rooms[i].TTL--
			}
		}
	}(ticker1)

	r := gin.New()
	r.Use(gin.Recovery())
	r.Use(Cors())

	r.GET("/api/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	r.GET("/api/rooms", func(c *gin.Context) {
		c.JSON(http.StatusOK, rooms)
	})

	r.GET("/api/room/:id", func(c *gin.Context) {
		id, err := strconv.Atoi(c.Param("id"))
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		// fmt.Println(id)

		idx := QueryRoomById(id, rooms)
		if idx < 0 {
			c.JSON(http.StatusNotFound, "Room does not exist")
			return
		}

		c.JSON(http.StatusOK, rooms[idx])

	})

	r.POST("/api/room", func(c *gin.Context) {
		// payload: userInfo of creator
		// name := c.PostForm("username")
		// id   := c.PostForm("id")

		obj := struct {
			Name string `json:"username"`
			Id   string `json:"id"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		// 新建游戏房间
		var room Room
		room.Id = roomMaxNum
		roomMaxNum++
		room.State = "INIT"
		room.OwnerId = obj.Id
		room.TTL = 300 // 300秒内无动作，则释放房间

		room.Candidates = []Candidate{
			{obj.Id, obj.Name, true, 0},
			{"", "", false, 0},
			{"", "", false, 0},
		}

		fmt.Println(room)
		fmt.Println(roomMaxNum)

		rooms = append(rooms, room)
		fmt.Println(rooms)
		c.JSON(http.StatusOK, room)
	})

	r.GET("/api/room/:id/observe", func(c *gin.Context) {
		// SSE：观察房间状态并广播

		id, err := strconv.Atoi(c.Param("id"))
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		idx := QueryRoomById(id, rooms)
		if idx < 0 {
			c.JSON(http.StatusNotFound, "Room does not exist")
			return
		}

		// c.JSON(http.StatusOK, rooms[idx])
		w := c.Writer

		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		w.Header().Set("Access-Control-Allow-Origin", "*")

		_, ok := w.(http.Flusher)
		if !ok {
			log.Panic("server not support") //浏览器不兼容
		}

		_, err = fmt.Fprintf(w, "foobar")
		if err != nil {
			return
		}
	})

	r.POST("/api/room/:id/ready", func(c *gin.Context) {
		// 组队完成，预备
		id, err := strconv.Atoi(c.Param("id"))
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		idx := QueryRoomById(id, rooms)
		if idx < 0 {
			c.JSON(http.StatusNotFound, "Room does not exist")
			return
		}

		if rooms[idx].State != "INIT" {
			c.JSON(http.StatusServiceUnavailable, "Incorrect Room State")
			return
		}

		// 根据Candidate补充玩家信息
		err = rooms[idx].startGame()
		if err != nil {
			// 组队不成功，返回失败
			c.JSON(http.StatusServiceUnavailable, "Not Ready")
			return
		}

		rooms[idx].State = "READY"

		// 组队成功，创建游戏
		c.JSON(http.StatusOK, rooms[idx])

	})

	r.PUT("/api/room/:id", func(c *gin.Context) {
		// 修改游戏房间属性：玩家数量、玩家类型
		id, err := strconv.Atoi(c.Param("id"))
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		var room Room
		c.ShouldBindJSON(room)

		// fmt.Println(id, room)

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates = room.Candidates
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}

		c.JSON(http.StatusNotFound, "")
	})

	r.POST("/api/room/add_candidate", func(c *gin.Context) {
		// 房间新增玩家，默认新增一个AI Bot
		obj := struct {
			RoomId string `json:"roomId"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates = append(rooms[i].Candidates, Candidate{"", "", false, 0})
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}

		c.JSON(http.StatusNotFound, fmt.Sprintf("Room %d does not exist", id))

	})

	r.POST("/api/room/delete_candidate", func(c *gin.Context) {
		// 房间删除候选玩家（房间第几位）
		obj := struct {
			RoomId string `json:"roomId"`
			SeatNo int    `json:"seatNo"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].removeCandidate(obj.SeatNo)
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}
	})

	r.POST("/api/room/change_candidate", func(c *gin.Context) {
		// 房间变更玩家类型（房间第几位）
		obj := struct {
			RoomId  string `json:"roomId"`
			SeatNo  int    `json:"seatNo"`
			IsHuman bool
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates[obj.SeatNo] = Candidate{"", "", obj.IsHuman, 0}
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}
	})

	r.POST("/api/room/change_bot_type", func(c *gin.Context) {
		// AI: 变更机器人类型
		obj := struct {
			RoomId  string `json:"roomId"`
			SeatNo  int    `json:"seatNo"`
			BotType int
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates[obj.SeatNo].BotType = obj.BotType
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}
	})

	r.POST("/api/room/candidate_seat", func(c *gin.Context) {
		// 人类玩家占座
		obj := struct {
			RoomId   string `json:"roomId"`
			SeatNo   int    `json:"seatNo"`
			UserName string
			UserId   string
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates[obj.SeatNo] = Candidate{obj.UserId, obj.UserName, true, 0}
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}

	})

	r.POST("/api/room/candidate_unseat", func(c *gin.Context) {
		// 人类玩家离开座位
		obj := struct {
			RoomId string `json:"roomId"`
			SeatNo int    `json:"seatNo"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		id, err := strconv.Atoi(obj.RoomId)
		if err != nil {
			c.JSON(http.StatusBadRequest, "Invalid Room Id")
			return
		}

		for i := 0; i < len(rooms); i++ {
			if rooms[i].Id == id {
				rooms[i].Candidates[obj.SeatNo] = Candidate{"", "", true, 1}
				c.JSON(http.StatusOK, rooms[i])
				return
			}
		}

	})

	r.POST("/api/play/action", func(c *gin.Context) {
		// 玩家下注
		obj := struct {
			RoomId   int    `json:"roomId"`
			GameId   int    `json:"gameId"`
			PlayerId string `json:"playerId"`
			Amount   int    `json:"amount"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		i := obj.RoomId - 1
		p := rooms[i].Games[0].GetPlayer(obj.PlayerId)
		if p != nil {
			log.Println("[API] Player Action: ", obj.PlayerId, obj.Amount)
			p.action <- Event{obj.PlayerId, obj.Amount, ""}
		}

	})

	r.POST("/api/play/choose", func(c *gin.Context) {
		// 玩家抉择：继续游戏还是退出
		obj := struct {
			RoomId   int    `json:"roomId"`
			GameId   int    `json:"gameId"`
			PlayerId string `json:"playerId"`
			Choice   string `json:"choice"`
		}{}

		err := c.BindJSON(&obj)
		if err != nil {
			log.Println(err)
			c.JSON(http.StatusBadRequest, "Invalid Parameters")
			return
		} else {
			log.Println(obj)
		}

		i := obj.RoomId - 1
		p := rooms[i].Games[0].GetPlayer(obj.PlayerId)
		if p != nil {
			log.Println("[API] Player Choice: ", p.Id, p.Name, obj.Choice)
			p.choice <- obj.Choice
		}
	})

	r.Run()

}
